/**
 * ImageColorExtractor Enhanced - A plugin to extract dominant colors from images
 * and generate harmonious gradient backgrounds with multiple style options
 */
class ImageColorExtractor {
    constructor(options = {}) {
        this.options = {
            container: null,
            gradientDirection: "to right",
            colorCount: 5,
            maxColors: 8,
            quality: 5, // Lower quality for better color extraction
            ...options,
        }

        this.canvas = document.createElement("canvas")
        this.ctx = this.canvas.getContext("2d")
        this.dominantColors = []
        this.gradientColors = []
        this.image = null

        // Export canvas
        this.exportCanvas = document.createElement("canvas")
        this.exportCtx = this.exportCanvas.getContext("2d")

        // State tracking
        this.currentStyleIndex = 0
        this.styles = [
            { type: "linear", name: "线性渐变" },
            { type: "radial", name: "径向渐变" },
            { type: "conic", name: "角度渐变" },
        ]

        this.init()
    }

    init() {
        if (typeof this.options.container === "string") {
            this.container = document.querySelector(this.options.container)
        } else {
            this.container = this.options.container
        }

        if (!this.container) {
            console.error("Container element not found")
            return
        }

        this.render()
        this.bindEvents()
        console.log("ImageColorExtractor initialized")
    }

    render() {
        // Create UI elements
        this.container.classList.add("color-extractor-container")

        // Create drop zone
        this.dropZone = document.createElement("div")
        this.dropZone.classList.add("drop-zone")
        this.dropZone.innerHTML = `
      <div class="drop-zone-content">
        <div class="icon-container">
          <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"></path>
            <line x1="16" y1="5" x2="22" y2="5"></line>
            <line x1="19" y1="2" x2="19" y2="8"></line>
            <circle cx="9" cy="9" r="2"></circle>
            <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path>
          </svg>
        </div>
        <p>拖放图片到这里或点击上传</p>
        <input type="file" accept="image/*" class="file-input" />
      </div>
    `

        // Create preview section
        this.previewSection = document.createElement("div")
        this.previewSection.classList.add("preview-section", "hidden")
        this.previewSection.innerHTML = `
      <div class="preview-header">
        <h2>提取到的颜色</h2>
        <button class="reset-button">重新选择图片</button>
      </div>
      
      <div class="preview-image-container">
        <img class="preview-image" />
      </div>
      
      <div class="color-palette"></div>
      
      <div class="controls">
        <div class="control-group">
          <label>渐变方向:</label>
          <select class="gradient-direction">
            <option value="to right">从左到右</option>
            <option value="to left">从右到左</option>
            <option value="to bottom">从上到下</option>
            <option value="to top">从下到上</option>
            <option value="to right bottom">左上到右下</option>
            <option value="to left bottom">右上到左下</option>
            <option value="to right top">左下到右上</option>
            <option value="to left top">右下到左上</option>
          </select>
        </div>
        
        <div class="control-group">
          <label>颜色数量:</label>
          <input type="range" min="2" max="8" value="5" class="color-count" />
          <span class="color-count-value">5</span>
        </div>
        
        <button class="apply-button">应用背景</button>
        <button class="copy-button">复制 CSS</button>
      </div>
    `

        // Create result section with side-by-side cards
        this.resultSection = document.createElement("div")
        this.resultSection.classList.add("result-section", "hidden")
        this.resultSection.innerHTML = `
      <h2>背景预览</h2>
      
      <div class="result-cards-container">
        <!-- 线性渐变卡片 -->
        <div class="result-card" data-style="linear">
          <div class="card-header">
            <h3>线性渐变</h3>
          </div>
          <div class="result-image-wrapper">
            <div class="gradient-background linear-gradient"></div>
            <img class="result-image" />
          </div>
          <div class="card-footer">
            <button class="export-button" data-style="linear">导出背景</button>
            <button class="copy-css-button" data-style="linear">复制CSS</button>
          </div>
        </div>
        
        <!-- 径向渐变卡片 -->
        <div class="result-card" data-style="radial">
          <div class="card-header">
            <h3>径向渐变</h3>
          </div>
          <div class="result-image-wrapper">
            <div class="gradient-background radial-gradient"></div>
            <img class="result-image" />
          </div>
          <div class="card-footer">
            <button class="export-button" data-style="radial">导出背景</button>
            <button class="copy-css-button" data-style="radial">复制CSS</button>
          </div>
        </div>
        
        <!-- 角度渐变卡片 -->
        <div class="result-card" data-style="conic">
          <div class="card-header">
            <h3>角度渐变</h3>
          </div>
          <div class="result-image-wrapper">
            <div class="gradient-background conic-gradient"></div>
            <img class="result-image" />
          </div>
          <div class="card-footer">
            <button class="export-button" data-style="conic">导出背景</button>
            <button class="copy-css-button" data-style="conic">复制CSS</button>
          </div>
        </div>
      </div>
      
      <div class="css-code">
        <div class="code-header">
          <h3>CSS 代码</h3>
          <button class="copy-code-button">复制</button>
        </div>
        <pre><code class="css-code-content"></code></pre>
      </div>
    `

        // Append elements to container
        this.container.appendChild(this.dropZone)
        this.container.appendChild(this.previewSection)
        this.container.appendChild(this.resultSection)

        // Get references to elements
        this.fileInput = this.container.querySelector(".file-input")
        this.previewImage = this.container.querySelector(".preview-image")
        this.resultImages = this.container.querySelectorAll(".result-image")
        this.colorPalette = this.container.querySelector(".color-palette")
        this.gradientDirectionSelect = this.container.querySelector(".gradient-direction")
        this.colorCountInput = this.container.querySelector(".color-count")
        this.colorCountValue = this.container.querySelector(".color-count-value")
        this.applyButton = this.container.querySelector(".apply-button")
        this.copyButton = this.container.querySelector(".copy-button")
        this.copyCodeButton = this.container.querySelector(".copy-code-button")
        this.cssCodeContent = this.container.querySelector(".css-code-content")
        this.resetButton = this.container.querySelector(".reset-button")
        this.exportButtons = this.container.querySelectorAll(".export-button")
        this.copyCssButtons = this.container.querySelectorAll(".copy-css-button")
        this.resultCards = this.container.querySelectorAll(".result-card")
        this.linearGradientBg = this.container.querySelector(".linear-gradient")
        this.radialGradientBg = this.container.querySelector(".radial-gradient")
        this.conicGradientBg = this.container.querySelector(".conic-gradient")

        // Set initial values
        this.gradientDirectionSelect.value = this.options.gradientDirection
        this.colorCountInput.value = this.options.colorCount
        this.colorCountValue.textContent = this.options.colorCount
    }

    bindEvents() {
        // File input change
        this.fileInput.addEventListener("change", (e) => {
            if (e.target.files && e.target.files[0]) {
                this.handleImageUpload(e.target.files[0])
            }
        })

        // Drop zone click
        this.dropZone.addEventListener("click", () => {
            this.fileInput.click()
        })

        // Drag and drop events
        this.dropZone.addEventListener("dragover", (e) => {
            e.preventDefault()
            this.dropZone.classList.add("drag-over")
        })

        this.dropZone.addEventListener("dragleave", () => {
            this.dropZone.classList.remove("drag-over")
        })

        this.dropZone.addEventListener("drop", (e) => {
            e.preventDefault()
            this.dropZone.classList.remove("drag-over")

            if (e.dataTransfer.files && e.dataTransfer.files[0]) {
                this.handleImageUpload(e.dataTransfer.files[0])
            }
        })

        // Reset button
        this.resetButton.addEventListener("click", () => {
            this.resetUpload()
        })

        // Controls events
        this.gradientDirectionSelect.addEventListener("change", () => {
            console.log("Direction changed:", this.gradientDirectionSelect.value)
            this.options.gradientDirection = this.gradientDirectionSelect.value
            this.updateGradient()
        })

        // 修复颜色数量滑块事件处理
        this.colorCountInput.addEventListener("input", () => {
            console.log("Color count changed:", this.colorCountInput.value)
            this.options.colorCount = Number.parseInt(this.colorCountInput.value)
            this.colorCountValue.textContent = this.options.colorCount
            this.generateGradientColors()
            this.updateGradient()
        })

        this.applyButton.addEventListener("click", () => {
            this.applyBackground()
        })

        this.copyButton.addEventListener("click", () => {
            this.copyCSS()
        })

        this.copyCodeButton.addEventListener("click", () => {
            this.copyCSS(true)
        })

        // Export buttons
        this.exportButtons.forEach((button) => {
            button.addEventListener("click", () => {
                const style = button.getAttribute("data-style")
                console.log("Export button clicked for style:", style)
                this.exportBackground(style)
            })
        })

        // Copy CSS buttons
        this.copyCssButtons.forEach((button) => {
            button.addEventListener("click", () => {
                const style = button.getAttribute("data-style")
                console.log("Copy CSS button clicked for style:", style)
                this.copyCSSForStyle(style)
            })
        })
    }

    resetUpload() {
        this.image = null
        this.dominantColors = []
        this.gradientColors = []
        this.fileInput.value = ""

        this.previewSection.classList.add("hidden")
        this.resultSection.classList.add("hidden")
        this.dropZone.classList.remove("hidden")

        // Reset body background if it was applied
        document.body.style.background = ""
    }

    handleImageUpload(file) {
        if (!file.type.match("image.*")) {
            alert("请上传图片文件")
            return
        }

        const reader = new FileReader()

        reader.onload = (e) => {
            this.image = new Image()
            this.image.onload = () => {
                this.previewImage.src = this.image.src
                this.resultImages.forEach((img) => {
                    img.src = this.image.src
                })

                this.extractColors()
                this.showPreview()
            }
            this.image.src = e.target.result
        }

        reader.readAsDataURL(file)
    }

    extractColors() {
        // Resize canvas to image dimensions (max 500px for performance)
        const maxDimension = 500
        let width = this.image.width
        let height = this.image.height

        if (width > height && width > maxDimension) {
            height = (height / width) * maxDimension
            width = maxDimension
        } else if (height > maxDimension) {
            width = (width / height) * maxDimension
            height = maxDimension
        }

        this.canvas.width = width
        this.canvas.height = height

        // Draw image on canvas
        this.ctx.drawImage(this.image, 0, 0, width, height)

        // Get image data
        const imageData = this.ctx.getImageData(0, 0, width, height)
        const pixels = imageData.data

        // Extract colors using K-means clustering
        this.dominantColors = this.extractColorsKMeans(pixels, this.options.maxColors)
        console.log("Extracted dominant colors:", this.dominantColors)

        // Generate gradient colors
        this.generateGradientColors()

        // Update UI
        this.updateColorPalette()
        this.updateGradient()
    }

    extractColorsKMeans(pixels, k) {
        // Collect all non-transparent pixels
        const pixelArray = []
        for (let i = 0; i < pixels.length; i += 4 * this.options.quality) {
            const r = pixels[i]
            const g = pixels[i + 1]
            const b = pixels[i + 2]
            const a = pixels[i + 3]

            // Skip transparent/semi-transparent pixels
            if (a < 128) continue

            pixelArray.push([r, g, b])
        }

        // If not enough pixels, return what we have
        if (pixelArray.length < k) {
            return pixelArray
        }

        // Initialize centroids randomly from the pixel data
        const centroids = []
        const usedIndices = new Set()

        for (let i = 0; i < k; i++) {
            let index
            // Ensure we don't pick the same pixel twice
            do {
                index = Math.floor(Math.random() * pixelArray.length)
            } while (usedIndices.has(index))

            usedIndices.add(index)
            centroids.push(pixelArray[index].slice())
        }

        // Run K-means algorithm
        const maxIterations = 10
        let iteration = 0
        let oldCentroids = null

        while (iteration < maxIterations) {
            // Assign pixels to nearest centroid
            const clusters = Array(k)
                .fill()
                .map(() => [])

            for (let i = 0; i < pixelArray.length; i++) {
                const pixel = pixelArray[i]
                let minDistance = Number.POSITIVE_INFINITY
                let closestCentroidIndex = 0

                for (let j = 0; j < k; j++) {
                    const centroid = centroids[j]
                    const distance = this.colorDistance(pixel, centroid)

                    if (distance < minDistance) {
                        minDistance = distance
                        closestCentroidIndex = j
                    }
                }

                clusters[closestCentroidIndex].push(pixel)
            }

            // Store old centroids for convergence check
            oldCentroids = [...centroids]

            // Update centroids
            for (let i = 0; i < k; i++) {
                const cluster = clusters[i]

                if (cluster.length === 0) continue

                const newCentroid = [0, 0, 0]

                for (let j = 0; j < cluster.length; j++) {
                    newCentroid[0] += cluster[j][0]
                    newCentroid[1] += cluster[j][1]
                    newCentroid[2] += cluster[j][2]
                }

                newCentroid[0] = Math.round(newCentroid[0] / cluster.length)
                newCentroid[1] = Math.round(newCentroid[1] / cluster.length)
                newCentroid[2] = Math.round(newCentroid[2] / cluster.length)

                centroids[i] = newCentroid
            }

            // Check for convergence
            let converged = true
            for (let i = 0; i < k; i++) {
                if (this.colorDistance(oldCentroids[i], centroids[i]) > 1) {
                    converged = false
                    break
                }
            }

            if (converged) break
            iteration++
        }

        // Calculate prominence of each centroid (size of its cluster)
        const clusterSizes = Array(k).fill(0)

        for (let i = 0; i < pixelArray.length; i++) {
            const pixel = pixelArray[i]
            let minDistance = Number.POSITIVE_INFINITY
            let closestCentroidIndex = 0

            for (let j = 0; j < k; j++) {
                const centroid = centroids[j]
                const distance = this.colorDistance(pixel, centroid)

                if (distance < minDistance) {
                    minDistance = distance
                    closestCentroidIndex = j
                }
            }

            clusterSizes[closestCentroidIndex]++
        }

        // Sort centroids by cluster size (prominence)
        const centroidsWithSize = centroids.map((centroid, index) => ({
            color: centroid,
            size: clusterSizes[index],
        }))

        centroidsWithSize.sort((a, b) => b.size - a.size)

        // Return sorted centroids (colors only)
        return centroidsWithSize.map((item) => item.color)
    }

    colorDistance(color1, color2) {
        // Simple Euclidean distance in RGB space
        const rDiff = color1[0] - color2[0]
        const gDiff = color1[1] - color2[1]
        const bDiff = color1[2] - color2[2]

        return Math.sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff)
    }

    generateGradientColors() {
        const count = this.options.colorCount
        console.log("Generating gradient colors, count:", count)

        // If we have enough dominant colors, use them
        if (this.dominantColors.length >= count) {
            this.gradientColors = this.dominantColors.slice(0, count)
            console.log("Using dominant colors for gradient:", this.gradientColors)
            return
        }

        // Otherwise, generate additional colors based on the main colors
        this.gradientColors = [...this.dominantColors]

        while (this.gradientColors.length < count) {
            // Take a random color from dominantColors and create a variation
            const baseColorIndex = Math.floor(Math.random() * this.dominantColors.length)
            const baseColor = this.dominantColors[baseColorIndex]

            // Convert to HSL for better color manipulation
            const hsl = this.rgbToHsl(baseColor[0], baseColor[1], baseColor[2])

            // Adjust hue slightly
            const hueShift = Math.random() * 30 - 15 // -15 to +15
            let newHue = (hsl[0] + hueShift) % 360
            if (newHue < 0) newHue += 360

            // Adjust saturation and lightness
            const newSat = Math.max(0.1, Math.min(1, hsl[1] + (Math.random() * 0.2 - 0.1)))
            const newLight = Math.max(0.1, Math.min(0.9, hsl[2] + (Math.random() * 0.2 - 0.1)))

            const newColor = this.hslToRgb(newHue, newSat, newLight)

            // Add the new color if it's different enough from existing colors
            let isDifferentEnough = true
            for (const color of this.gradientColors) {
                if (this.colorDistance(newColor, color) < 20) {
                    isDifferentEnough = false
                    break
                }
            }

            if (isDifferentEnough) {
                this.gradientColors.push(newColor)
            }
        }

        console.log("Generated gradient colors:", this.gradientColors)
    }

    updateColorPalette() {
        this.colorPalette.innerHTML = ""

        this.dominantColors.forEach((color) => {
            const colorBox = document.createElement("div")
            colorBox.classList.add("color-box")
            colorBox.style.backgroundColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`

            const colorValue = document.createElement("span")
            colorValue.classList.add("color-value")
            colorValue.textContent = this.rgbToHex(color[0], color[1], color[2])

            colorBox.appendChild(colorValue)
            this.colorPalette.appendChild(colorBox)
        })
    }

    updateGradient() {
        if (!this.image) return

        // Regenerate gradient colors if needed
        if (this.gradientColors.length !== this.options.colorCount) {
            this.generateGradientColors()
        }

        // Update all gradient styles
        this.updateLinearGradient()
        this.updateRadialGradient()
        this.updateConicGradient()

        // Update CSS code for the current style
        this.updateCSSCode("linear")
    }

    updateLinearGradient() {
        const gradientCSS = this.getLinearGradientCSS().replace("background: ", "")
        this.linearGradientBg.style.background = gradientCSS
    }

    updateRadialGradient() {
        const gradientCSS = this.getRadialGradientCSS().replace("background: ", "")
        this.radialGradientBg.style.background = gradientCSS
    }

    updateConicGradient() {
        const gradientCSS = this.getConicGradientCSS().replace("background: ", "")
        this.conicGradientBg.style.background = gradientCSS
    }

    getLinearGradientCSS() {
        const gradientColors = this.gradientColors
            .map((color, index) => {
                const percent = index * (100 / (this.gradientColors.length - 1))
                return `rgb(${color[0]}, ${color[1]}, ${color[2]}) ${percent}%`
            })
            .join(", ")

        return `background: linear-gradient(${this.options.gradientDirection}, ${gradientColors});`
    }

    getRadialGradientCSS() {
        const gradientColors = this.gradientColors
            .map((color, index) => {
                const percent = index * (100 / (this.gradientColors.length - 1))
                return `rgb(${color[0]}, ${color[1]}, ${color[2]}) ${percent}%`
            })
            .join(", ")

        return `background: radial-gradient(circle, ${gradientColors});`
    }

    getConicGradientCSS() {
        const gradientColors = this.gradientColors
            .map((color, index) => {
                const percent = index * (360 / this.gradientColors.length)
                return `rgb(${color[0]}, ${color[1]}, ${color[2]}) ${percent}deg`
            })
            .join(", ")

        return `background: conic-gradient(from 0deg, ${gradientColors});`
    }

    updateCSSCode(style) {
        let cssCode = ""

        switch (style) {
            case "linear":
                cssCode = this.getLinearGradientCSS()
                break
            case "radial":
                cssCode = this.getRadialGradientCSS()
                break
            case "conic":
                cssCode = this.getConicGradientCSS()
                break
        }

        this.cssCodeContent.textContent = cssCode
    }

    applyBackground() {
        this.resultSection.classList.remove("hidden")
        document.body.style.background = this.getLinearGradientCSS().replace("background: ", "")
        this.resultSection.scrollIntoView({ behavior: "smooth" })
    }

    exportBackground(styleType) {
        console.log("Exporting background:", styleType)

        // 创建高分辨率画布
        const width = 1920
        const height = 1080

        this.exportCanvas.width = width
        this.exportCanvas.height = height

        // 根据样式创建渐变
        let gradient

        switch (styleType) {
            case "linear":
                gradient = this.createLinearGradient(this.exportCtx, width, height)
                break
            case "radial":
                gradient = this.createRadialGradient(this.exportCtx, width, height)
                break
            case "conic":
                gradient = this.createConicGradient(this.exportCtx, width, height)
                break
            default:
                gradient = this.createLinearGradient(this.exportCtx, width, height)
        }

        // 用渐变填充画布
        this.exportCtx.fillStyle = gradient
        this.exportCtx.fillRect(0, 0, width, height)

        // 转换为数据URL并触发下载
        try {
            const dataURL = this.exportCanvas.toDataURL("image/png")
            const link = document.createElement("a")
            link.href = dataURL
            link.download = `gradient-${styleType}-${Date.now()}.png`
            document.body.appendChild(link)
            link.click()
            document.body.removeChild(link)

            // 显示成功通知
            this.showNotification("背景已成功导出")
        } catch (e) {
            console.error("导出图片失败", e)
            alert("导出图片失败，请检查控制台获取详细信息。")
        }
    }

    createLinearGradient(ctx, width, height) {
        let gradient

        // Create gradient based on direction
        switch (this.options.gradientDirection) {
            case "to right":
                gradient = ctx.createLinearGradient(0, 0, width, 0)
                break
            case "to left":
                gradient = ctx.createLinearGradient(width, 0, 0, 0)
                break
            case "to bottom":
                gradient = ctx.createLinearGradient(0, 0, 0, height)
                break
            case "to top":
                gradient = ctx.createLinearGradient(0, height, 0, 0)
                break
            case "to right bottom":
                gradient = ctx.createLinearGradient(0, 0, width, height)
                break
            case "to left bottom":
                gradient = ctx.createLinearGradient(width, 0, 0, height)
                break
            case "to right top":
                gradient = ctx.createLinearGradient(0, height, width, 0)
                break
            case "to left top":
                gradient = ctx.createLinearGradient(width, height, 0, 0)
                break
            default:
                gradient = ctx.createLinearGradient(0, 0, width, 0)
        }

        // Add color stops
        this.gradientColors.forEach((color, index) => {
            const offset = index / (this.gradientColors.length - 1)
            gradient.addColorStop(offset, `rgb(${color[0]}, ${color[1]}, ${color[2]})`)
        })

        return gradient
    }

    createRadialGradient(ctx, width, height) {
        const centerX = width / 2
        const centerY = height / 2
        const radius = Math.max(width, height) / 2

        const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius)

        // Add color stops
        this.gradientColors.forEach((color, index) => {
            const offset = index / (this.gradientColors.length - 1)
            gradient.addColorStop(offset, `rgb(${color[0]}, ${color[1]}, ${color[2]})`)
        })

        return gradient
    }

    createConicGradient(ctx, width, height) {
        // Polyfill for browsers that don't support createConicGradient
        if (!ctx.createConicGradient) {
            return this.createRadialGradient(ctx, width, height)
        }

        const centerX = width / 2
        return this.createRadialGradient(ctx, width, height)

        const centerXVal = width / 2
        const centerY = height / 2

        const gradient = ctx.createConicGradient(0, centerXVal, centerY)

        // Add color stops
        this.gradientColors.forEach((color, index) => {
            const offset = index / this.gradientColors.length
            gradient.addColorStop(offset, `rgb(${color[0]}, ${color[1]}, ${color[2]})`)
        })

        return gradient
    }

    copyCSS(fromCodeBlock = false) {
        let css

        if (fromCodeBlock) {
            css = this.cssCodeContent.textContent
        } else {
            css = this.getLinearGradientCSS()
        }

        navigator.clipboard
            .writeText(css)
            .then(() => {
                this.showNotification("CSS已复制到剪贴板")
            })
            .catch((err) => {
                console.error("复制失败:", err)
                alert("复制失败，请手动复制代码。")
            })
    }

    copyCSSForStyle(style) {
        let css = ""

        switch (style) {
            case "linear":
                css = this.getLinearGradientCSS()
                break
            case "radial":
                css = this.getRadialGradientCSS()
                break
            case "conic":
                css = this.getConicGradientCSS()
                break
        }

        navigator.clipboard
            .writeText(css)
            .then(() => {
                this.showNotification(`${this.getStyleName(style)}CSS已复制到剪贴板`)
                this.updateCSSCode(style)
            })
            .catch((err) => {
                console.error("复制失败:", err)
                alert("复制失败，请手动复制代码。")
            })
    }

    getStyleName(style) {
        switch (style) {
            case "linear":
                return "线性渐变"
            case "radial":
                return "径向渐变"
            case "conic":
                return "角度渐变"
            default:
                return ""
        }
    }

    showNotification(message) {
        const notification = document.createElement("div")
        notification.classList.add("copy-notification")
        notification.textContent = message

        this.container.appendChild(notification)

        setTimeout(() => {
            notification.classList.add("show")
        }, 10)

        setTimeout(() => {
            notification.classList.remove("show")
            setTimeout(() => {
                this.container.removeChild(notification)
            }, 300)
        }, 2000)
    }

    showPreview() {
        this.dropZone.classList.add("hidden")
        this.previewSection.classList.remove("hidden")
    }

    // Utility functions
    rgbToHex(r, g, b) {
        return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`
    }

    rgbToHsl(r, g, b) {
        r /= 255
        g /= 255
        b /= 255

        const max = Math.max(r, g, b)
        const min = Math.min(r, g, b)
        let h,
            s,
            l = (max + min) / 2

        if (max === min) {
            h = s = 0 // achromatic
        } else {
            const d = max - min
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min)

            switch (max) {
                case r:
                    h = (g - b) / d + (g < b ? 6 : 0)
                    break
                case g:
                    h = (b - r) / d + 2
                    break
                case b:
                    h = (r - g) / d + 4
                    break
            }

            h /= 6
        }

        return [h * 360, s, l]
    }

    hslToRgb(h, s, l) {
        h /= 360
        let r, g, b

        if (s === 0) {
            r = g = b = l // achromatic
        } else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1
                if (t > 1) t -= 1
                if (t < 1 / 6) return p + (q - p) * 6 * t
                if (t < 1 / 2) return q
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
                return p
            }

            const q = l < 0.5 ? l * (1 + s) : l + s - l * s
            const p = 2 * l - q

            r = hue2rgb(p, q, h + 1 / 3)
            g = hue2rgb(p, q, h)
            b = hue2rgb(p, q, h - 1 / 3)
        }

        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]
    }
}

